package com.wl.lib.utils

import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import okhttp3.OkHttpClient
import okhttp3.Request
import okio.buffer
import okio.sink
import java.io.File
import java.io.IOException
import java.util.concurrent.TimeUnit


/**
 *
 * Created by wangliang on 2024/11/26
 */
object FileDownloader {
    private const val DEFAULT_BUFFER_SIZE = 8 * 1024 // 8 KB

    private val client = OkHttpClient().newBuilder()
        .connectTimeout(30, TimeUnit.SECONDS) // 设置连接超时时间
        .readTimeout(5, TimeUnit.MINUTES)     // 设置读取超时时间（适合大文件）
        .writeTimeout(5, TimeUnit.MINUTES)    // 设置写入超时时间
        .retryOnConnectionFailure(true)
        .build()

    /**
     * 下载多个文件
     * @param downloads 包含下载 URL 和目标路径的列表
     * @param onProgress 每个文件的进度回调 (url, progress)
     */
    suspend fun downloadFiles(
        downloads: List<String>,
        rootPath: String,
        onProgress: (url: String, progress: Double) -> Unit,
        onFinished: (url: String) -> Unit,
        needUnzip: Boolean = false,
    ) {
        coroutineScope {
            // 使用 async 并发下载多个文件
            val jobs = downloads.map { url ->
                async(Dispatchers.IO) {
                    val filename = FileUtils.getFilenameFromUrl(url)
                    val destinationPath = rootPath + filename
                    if (FileUtils.isFileExists(destinationPath)) {
                        Log.d("WLTest", "文件已存在: $destinationPath")
                        onFinished.invoke(url)
                        return@async
                    }
                    Log.d("WLTest", "下载文件: $url -> $destinationPath")
                    downloadFile(url, destinationPath, { progress ->
                        onProgress(url, progress)
                    }, {
                        if (needUnzip) {

                            val distFile = File(rootPath + FileUtils.getFilenameWithoutSuffix(filename))
                            if (!distFile.exists()) {
                                distFile.mkdirs()
                            }
                            Log.d("WLTest", "解压 $destinationPath")
                            FileUtils.unzip(destinationPath, distFile.absolutePath)
                            Log.d("WLTest", "解压完成 $destinationPath")
//                            FileUtils.deleteFile(destinationPath)
                            onFinished(url)
                        } else {
                            onFinished(url)
                        }
                    })

                }
            }
            // 等待所有文件下载完成
            jobs.awaitAll()
        }
    }

    /**
     * 下载单个文件
     * @param url 文件下载链接
     * @param destinationPath 文件保存路径
     * @param onProgress 下载进度回调
     */
    private suspend fun downloadFile(
        url: String,
        destinationPath: String,
        onProgress: (progress: Double) -> Unit,
        onFinished: () -> Unit
    ) {
        val request = Request.Builder().url(url).build()
        val response = client.newCall(request).execute()

        if (!response.isSuccessful) {
            throw IOException("Failed to download file: ${response.code}")
        }

        val file = File(destinationPath)
        val body = response.body ?: throw IOException("Response body is null")

        body.use { responseBody ->
            val contentLength = responseBody.contentLength()
            var bytesCopied = 0L

            file.sink().buffer().use { sink ->
                responseBody.byteStream().use { inputStream ->
                    val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
                    var bytesRead: Int
                    while (inputStream.read(buffer).also { bytesRead = it } != -1) {
                        sink.write(buffer, 0, bytesRead)
                        bytesCopied += bytesRead

                        if (contentLength > 0) {
                            val progress = bytesCopied.toDouble() / contentLength
                            onProgress(progress)
                        }
                    }
                }
            }
        }
        onFinished()
    }
}